package duppentity;

import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import constants.Configurations;
import constants.Default;
import duppentity.annotations.Dupp;
import duppentity.annotations.Param;
import duppentity.convertors.Convertor;
import play.Logger;
import play.Play;
import play.cache.Cache;
import play.cache.CacheFor;
import play.libs.Codec;
import org.apache.commons.lang.StringUtils;
import utils.DuppEntityUtil;
import utils.Utils;

import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Comparator;
import java.util.Map;
import java.util.TreeMap;

/**
 * Dupp接口实现
 * @author zhangzc@djcars.cn
 * @create 2016/8/8
 */
public class DuppImplement implements InvocationHandler {

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Long startTime = System.currentTimeMillis();
        String interfaces = "";
        try{
            //Dupp请求参数表
            Map<String,Object> kv = new TreeMap(new MapValueComparator());
            //1. 获取请求接口地址
            Dupp dupp = method.getAnnotation(Dupp.class);
            CacheFor cacheFor = method.getAnnotation(CacheFor.class);
            interfaces = dupp.value();
            if(dupp == null){
                throw new RuntimeException("Dupp接口的方法["+method.getName()+"]必须注解@Dupp");
            }

            //2. 根据@Param注解获取请求参数
            if(args != null){
                for (int i = 0; i < args.length; i++) {
                    Object arg = args[i];
                    if(arg == null){ //忽略null值
                        continue;
                    }
                    Annotation[] annotations = method.getParameterAnnotations()[i];
                    for(Annotation a: annotations){
                        if(a.annotationType().equals(Param.class)){
                            Param paramAnn= (Param) a;
                            if(paramAnn == null){
                                throw new RuntimeException("Dupp接口的方法["+method.getName()+"]的参数必须注解@Param");
                            }

                            //放入Dupp请求参数表 key对应的value参数为空不传
                            if (StringUtils.isNotEmpty(paramAnn.value()) && StringUtils.isNotEmpty(arg.toString())) {
                                kv.put(paramAnn.value(), arg);
                            }
                        }
                    }
                }
            }
            //3. 请求Dupp接口
            if(cacheFor != null && Play.mode.isProd()){//尝试从缓存中取
                String key = dupp.value() + "-" + calculateMap(kv);
                String jsonInCache = Cache.get(key, String.class);
                if(StringUtils.isNotEmpty(jsonInCache)){//取到,返回
                    Logger.info("Dupp[%s]缓存：%s",dupp.value(), jsonInCache);
                    JsonObject result = new JsonParser().parse(jsonInCache).getAsJsonObject();
                    Object parsedResult = convert(result, dupp, method);
                    return parsedResult;
                }else{//取不到
                    DuppResult duppResult = fetch(method, dupp, kv);
                    Cache.set(key, duppResult.originResult.toString(), cacheFor.value());
                    return duppResult.parsedResult;
                }

            }else{//直接获取
                return fetch(method, dupp, kv).parsedResult;
            }

        }catch (Throwable e){
            if(e instanceof DuppException){
                throw e;
            }
            Logger.error(e, "调用DUPP[%s]失败, 参数:%s", method.getName(), StringUtils.join(args, ","));
            return null;
        }finally {
            Logger.info("调用DUPP接口：%s,响应时间：%s %s",interfaces,(System.currentTimeMillis() - startTime),"ms");
        }
    }

    private String calculateMap(Map<String, Object> map){
        StringBuilder sb = new StringBuilder();
        for(Map.Entry<String, Object> e: map.entrySet()){
            sb.append(e.getKey());
            sb.append("=>");
            sb.append(e.getValue() == null ? "" : e.getValue());
            sb.append("\n");
        }
//        Logger.info("calculateMap: %s", sb);
        String k = sb.toString();
        return Codec.hexMD5(k) + '-' + Codec.hexSHA1(k);
    }

    private Object convert(JsonObject result, Dupp dupp, Method method) throws IllegalAccessException, InstantiationException {
        Class<? extends Convertor> convertorClass = dupp.convertor();
        Convertor convertor = convertorClass.newInstance();
        return convertor.parse(method, result);
    }

    private DuppResult fetch(Method method, Dupp dupp, Map<String, Object> kv) throws IllegalAccessException, InstantiationException {
        String caller = "";
        Integer number = Utils.getDuppNumber(dupp.value());
        if(number.equals(Default.callerTypeNumber.one)) {
            caller = Configurations.duppCaller;
        }else{
            caller = Configurations.duppSaccCaller;
        }
        JsonObject result = DuppEntityUtil.getEntityWithRaw(caller,dupp.value(), kv);
        Object parsedResult = convert(result, dupp, method);
        return new DuppResult(result, parsedResult);
    }

    static class DuppResult{

        JsonObject originResult;

        Object parsedResult;

        public DuppResult(JsonObject originResult, Object parsedResult) {
            this.originResult = originResult;
            this.parsedResult = parsedResult;
        }
    }

    static class MapValueComparator implements Comparator<String> {

        @Override
        public int compare(String str1, String str2) {
            return str1.compareTo(str2);
        }
    }

}
